package com.google.code.easyparking.metier;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

/**
 * Représentation d'un parking.
 * @author Arnaud Versini
 *
 */
public class Parking implements IParking {
	
	/**
	 * Mutex d'accées aux ressources.
	 */
	private final Object mutexAccesRessourcesParking = new Object();
	
	/**
	 * Liste des places de parking.
	 */
	private final PlaceParking[] placesParking;
	
	/**
	 * Liste des entrées du parking
	 */
	private final AccesParking[] listeAccesParking;
	
	/**
	 * Liste des logs
	 */
	private final ArrayList<LogParking> logsParking = new ArrayList<LogParking>();
	
	private final HashSet<IVehicule> listeVehiculesPresents = new HashSet<IVehicule>();

	private final String nom;
	
	/**
	 * Constructeur de la classe Parking
	 * @param nom
	 * @param nbPlacesTotal
	 * @param nbEntrees
	 */
	public Parking(String nom, int nbPlacesTotal, int nbEntrees) {
		this.nom = nom ;
		this.placesParking = new PlaceParking[nbPlacesTotal];
		this.listeAccesParking = new AccesParking[nbEntrees];
		for(int i = 0; i < nbEntrees; i++) {
			listeAccesParking[i] = new AccesParking(i);
		}
		
		for(int i = 0; i < nbPlacesTotal; i++) {
			placesParking[i] = new PlaceParking(i);
		}
	}
	
	/**
	 * Retourne le nom du parking
	 */
	public String getNom() {
		return nom;
	}

	/**
	 * Méthode retournant le nombre de places libres
	 * @return int
	 */
	public int getNbPlacesLibre() {
		int nbPlacesVide = 0;
		synchronized (mutexAccesRessourcesParking) {
			for (final PlaceParking place : placesParking) {
				if (place.isFree()) {
					nbPlacesVide++;
				}
			}
			
		}
		return nbPlacesVide;
	}
	/**
	 * Permet de garer un vehicule à une place
	 * @param vehicule
	 * @return
	 * @throws FullParkingException 
	 * @throws VehiculeAlreadyInParkingException 
	 */
	private IPlaceParking garerVehicule(IVehicule vehicule) throws FullParkingException, VehiculeAlreadyInParkingException {
		if (listeVehiculesPresents.contains(vehicule))
			throw new VehiculeAlreadyInParkingException();
		IPlaceParking retValue = null;
		synchronized (mutexAccesRessourcesParking) {
			for (final PlaceParking place : placesParking) {
				if (place.isFree()) {
					retValue = place;
					place.attribuerVehicule(vehicule);
					listeVehiculesPresents.add(vehicule);
					break ;
				}
			}
		}
		if (retValue == null)
			throw new FullParkingException();
		return retValue;
	}
	
	/**
	 * Recherche la place du vehicule et libère la place
	 * @param vehicule
	 */
	private void quiterParking(IVehicule vehicule) {
		synchronized (mutexAccesRessourcesParking) {
			for (final PlaceParking place : placesParking) {
				if ( !place.isFree() && place.getVehiculeActuel().equals(vehicule)) {
					libererPlace(place);
					listeVehiculesPresents.remove(vehicule);
					break ;
				}
			}
		}
	}
	
	/**
	 * Libération de la place et ajout d'un item dans le log (dans arraylist (LogsParking)
	 * @param placeParking
	 */
	private void libererPlace(IPlaceParking placeParking) {
		Date dateHeureDepart = new Date();
		Date dateHeureArrive = placeParking.getDateArriveVehicule();
		LogParking log = new LogParking(dateHeureArrive, dateHeureDepart, placeParking.getVehiculeActuel(), placeParking);
		logsParking.add(log);
		placeParking.libererPlace();
		
	}
	
	/**
	 * Classe privée  pour gérer les entrées.
	 * @author franck
	 *
	 */
	private final class AccesParking implements IAccesParking {
		
		private final Camera camera = new Camera(this);
		private AccesParking(int numero) {
			this.numero = numero;
		}
		
		private int numero ;
		
		@Override
		public int getNumero() {
			return numero;
		}
		
		@Override
		public ICamera getCamera () {
			return camera;
		}

		/**
		 * 
		 * @param _vehiculeAGarer
		 * @return
		 * @throws FullParkingException 
		 * @throws VehiculeAlreadyInParkingException 
		 */
		@Override
		public IPlaceParking garerVehicule(IVehicule  _vehiculeAGarer) throws FullParkingException, VehiculeAlreadyInParkingException {
			return Parking.this.garerVehicule(_vehiculeAGarer);
		}
		/**
		 * 
		 * @param _vehicule
		 */
		@Override
		public void quitterParking(IVehicule  _vehicule) {
			quiterParking(_vehicule);
		}
	}

	@Override
	public IAccesParking getAccesParking(int numeroEntree) {
		return listeAccesParking[numeroEntree];
	}

	@Override
	public Iterator<LogParking> iterator() {
		return logsParking.iterator();
	}

	@Override
	public int getNbLogEvent() {
		return logsParking.size();
	}
}
